use crate::MathUtils::MathUtils;
use crate::Point::Point;
use crate::Segment::Segment;
use crate::Vector2::Vector2;

/**
 * 一条无限长的直线
 */
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Line2 {
    pub p: Point,
    pub normal: Vector2,
}

impl Line2 {
    pub fn new(p: Point, nor: Vector2) -> Self {
        Self {
            p,
            normal: nor.normalize(),
        }
    }

    pub fn new_with_point(p: Point, nor: Point) -> Self {
        Self {
            p,
            normal: nor.subtract(&p).ccw_normal().normalize(),
        }
    }

    pub fn dir(&self) -> Vector2 {
        self.normal.cw_normal()
    }

    /**
     * Get coefficients [A,B,C] of a standard line equation in the form Ax + By = C
     * @code [A, B, C] = line.standard
     * @returns {number[]} - array of coefficients
     */
    pub fn standard(&self) -> [f64; 3] {
        let a = self.normal.x;
        let b = self.normal.y;
        let c = self.normal.dot(&Vector2::new(self.p.x, self.p.y));

        [a, b, c]
    }

    pub fn parallel(&self, other: &Line2, ep: Option<f64>) -> bool {
        MathUtils::equal_0(self.normal.cross(&other.normal), ep)
    }

    pub fn intersect_line(&self, line: &Line2, ep: Option<f64>) -> Option<Point> {
        let [a1, b1, c1] = self.standard();
        let [a2, b2, c2] = line.standard();

        /* Cramer's rule */
        let det = a1 * b2 - b1 * a2;
        let det_x = c1 * b2 - b1 * c2;
        let det_y = a1 * c2 - c1 * a2;

        if !MathUtils::equal_0(det, ep) {
            let x;
            let y;

            if b1 == 0.0 {
                // 判断 === 0 要不要使用MathUtils.equalZero,待验证
                // vertical line x  = C1/A1, where A1 == +1 or -1
                x = c1 / a1;
                y = det_y / det;
            } else if b2 == 0.0 {
                // vertical line x = C2/A2, where A2 = +1 or -1
                x = c2 / a2;
                y = det_y / det;
            } else if a1 == 0.0 {
                // horizontal line y = C1/B1, where B1 = +1 or -1
                x = det_x / det;
                y = c1 / b1;
            } else if a2 == 0.0 {
                // horizontal line y = C2/B2, where B2 = +1 or -1
                x = det_x / det;
                y = c2 / b2;
            } else {
                x = det_x / det;
                y = det_y / det;
            }

            Some(Point::new(x, y))
        } else {
            None // 平行
        }
    }

    pub fn intersect_seg(&self, seg: &Segment, ep: Option<f64>) -> Option<Point> {
        let intersect_p = self.intersect_line(&seg.line(), ep)?;
        if !seg.contain(&intersect_p, ep) {
            return None;
        }
        Some(intersect_p)
    }

    pub fn distance_to_line(&self, p: &Point) -> f64 {
        let start_v = p.subtract(&self.p);
        start_v.dot(&self.normal)
    }

    pub fn closest_point(&self, p: &Point) -> Point {
        let n = self.dir();
        let start_v = p.subtract(&self.p);
        let translate_length = n.dot(&start_v);
        self.p.translate(&n.multiply(translate_length))
    }

    pub fn get_x_from_y(&self, y: f64, ep: Option<f64>) -> f64 {
        if MathUtils::equal_0(self.dir().y, ep) {
            return self.p.x; //  平行
        }
        if MathUtils::equal_0(self.dir().x, ep) {
            return self.p.x; // 垂直
        }
        let m = self.dir().y / self.dir().x;
        let b = self.p.y - self.p.x * m;
        let x = (y - b) / m;
        x
    }

    pub fn get_y_from_x(&self, x: f64, ep: Option<f64>) -> f64 {
        if MathUtils::equal_0(self.dir().y, ep) {
            return self.p.y; //  水平
        }
        if MathUtils::equal_0(self.dir().x, ep) {
            return self.p.y; // 垂直
        }
        let m = self.dir().y / self.dir().x;
        let b = self.p.y - self.p.x * m;
        let y = m * x + b;
        y
    }
}
